package upnp

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"sort"
	"time"
)

var listenConn *net.UDPConn

func init() {
	var err error
	listenConn, err = net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 7734,
	})
	if err != nil {
		panic(err)
	}
}

// SearchRoot search root device
func (c *Client) SearchRoot(wait uint) ([]*Device, error) {
	if wait > 5 {
		wait = 5
	}
	req, err := http.NewRequest("M-SEARCH", "*", nil)
	if err != nil {
		return nil, err
	}
	req.Host = "239.255.255.250:1900"
	req.Header.Set("ST", "upnp:rootdevice")
	req.Header.Set("MAN", `"ssdp:discover"`)
	req.Header.Set("MX", fmt.Sprintf("%d", wait))
	r, w := io.Pipe()
	defer r.Close()
	defer w.Close()
	go func() {
		req.Write(w)
		w.Close()
	}()
	if err := c.sendFrom(listenConn, &net.UDPAddr{
		IP:   net.IPv4(239, 255, 255, 250),
		Port: 1900,
	}, r); err != nil {
		return nil, err
	}

	after := time.After(time.Duration(wait) * time.Second)
	ret := make(map[string]bool)
	for {
		select {
		case <-after:
			var list []*Device
			for addr := range ret {
				list = append(list, NewDevice(addr))
			}
			sort.Slice(list, func(i, j int) bool {
				u1, _ := url.Parse(list[i].descLocation)
				u2, _ := url.Parse(list[j].descLocation)
				addr1 := net.ParseIP(u1.Hostname())
				addr2 := net.ParseIP(u2.Hostname())
				if addr1 != nil && addr2 != nil {
					return bytes.Compare(addr1.To4(), addr2.To4()) < 0
				}
				return true
			})
			return list, nil
		default:
			listenConn.SetReadDeadline(time.Now().Add(time.Duration(wait) * time.Second))
			resp, err := http.ReadResponse(bufio.NewReader(listenConn), req)
			if err == nil {
				defer resp.Body.Close()
				ret[resp.Header.Get("Location")] = true
			}
		}
	}
}
